package com.aizhixin.lab.jsrun.service;

import com.aizhixin.lab.common.core.ApiReturnConstants;
import com.aizhixin.lab.jsrun.util.Base64Util;
import com.aizhixin.lab.jsrun.util.HttpUtil;
import com.aizhixin.lab.jsrun.util.Md5Util;
import com.aizhixin.lab.jsrun.domian.CodeDomain;
import com.aizhixin.lab.jsrun.util.UnusualCode;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import okhttp3.FormBody;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.math.BigInteger;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
@Service
@Transactional
public class JsRunService {
    static final String APP_ID = "4";
    static final String APP_SECRET = "9bd7bb0c2e016d3ac6aafb6737862f0b";

    /**
     * jsRun代码执行对接接口
     *错误匹配
     * 编码风格效验
     * @param domain
     * @return
     */
    public ResponseEntity<?> jsRun(CodeDomain domain) {
        /**
         * 这里的code 要用于计算sign值，必须是还原成明文。
         * 部分 sign 异常原因是因为 code 值为 base64加密串或encodeURIComponent未被还原造成的
         */
        Map<String, Object> result = new HashMap<String, Object>();
        String sign = sign(domain.getCode());
        String lang = domain.getLang();

        // Post 请求
        FormBody body = new FormBody.Builder()
                .add("appId", APP_ID)
                .add("sign", sign)
                .add("lang", lang)
                .add("code", URLEncoder.encode(Base64Util.encode(domain.getCode())))
                .build();
        String restr = null;
        try {
            restr = HttpUtil.post("http://jsrun.net/api/run", body);
            if (result != null) {
                JSONObject jsonObject = JSONObject.parseObject(restr);
                Map jsonMap = jsonObject;
                //接口查询余额不足
                if (Integer.parseInt(jsonMap.get("error").toString()) == 41001) {
                    result.put(ApiReturnConstants.CODE, Boolean.FALSE);
                    result.put(ApiReturnConstants.MESSAGE, "查询接口受限，请联系管理员！");
                    return new ResponseEntity<Map<String, Object>>(result, HttpStatus.OK);
                }
                String resultStr = langConstructors(domain.getCode(), jsonMap.get("data").toString(), domain.getLang());
                result.put(ApiReturnConstants.CODE, Boolean.FALSE);
                result.put(ApiReturnConstants.MESSAGE, resultStr);
                return new ResponseEntity<Map<String, Object>>(result, HttpStatus.OK);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public Map codeRunDetection(CodeDomain domain) {
        /**
         * 这里的code 要用于计算sign值，必须是还原成明文。
         * 部分 sign 异常原因是因为 code 值为 base64加密串或encodeURIComponent未被还原造成的
         */
        Map<String, Object> result = new HashMap<String, Object>();
        //String sign = sign(APP_ID);
        String code = "";
        code = domain.getCode();
        String sign = sign(code,"");
        String lang = domain.getLang();

        // post 请求
        FormBody body = new FormBody.Builder()
                .add("appId", APP_ID)
                .add("sign", sign)
                .add("lang", lang)
                .add("code", URLEncoder.encode(Base64Util.encode(domain.getCode())))
                .add("input", URLEncoder.encode(Base64Util.encode("")))
                .build();
        String restr = null;
        try {
            restr = HttpUtil.post("http://jsrun.net/api/run", body);
            if (result != null) {
                JSONObject jsonObject = JSONObject.parseObject(restr);
                Map jsonMap = jsonObject;
                //接口查询余额不足
                if (Integer.parseInt(jsonMap.get("error").toString()) == 41001) {
                    result.put("data", "查询接口受限，请联系管理员！");
                    return result;
                }
                Map resultMap = langConstructorsToMap(domain.getCode(), jsonMap.get("data").toString(), domain.getLang());
                return resultMap;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /*public static void main(String[] args) {
        Map<String, Object> result = new HashMap<String, Object>();
        //String sign = sign(APP_ID);
        String code = "class Main {\n" +
                "\tpublic static void main(String[] args) {\n" +
                "        //JSRUN引擎2.0，支持多达30种语言在线运行，全仿真在线交互输入输出。\n" +
                "\t\tSystem.out.println(\"Hello JSRUN!   \\n\\n         - from JAVA .\");\n" +
                "\t}\n" +
                "}";
        String sign = sign(code,"");
        String lang = "java";

        // post 请求
        FormBody body = new FormBody.Builder()
                .add("appId", APP_ID)
                .add("sign", sign)
                .add("lang", lang)
                .add("code", URLEncoder.encode(Base64Util.encode(code)))
                .add("input", URLEncoder.encode(Base64Util.encode("")))
                .build();
        String restr = null;
        try {
            restr = HttpUtil.post("http://jsrun.net/api/run", body);
            if (result != null) {
                JSONObject jsonObject = JSONObject.parseObject(restr);
                Map jsonMap = jsonObject;
                //接口查询余额不足
                if (Integer.parseInt(jsonMap.get("error").toString()) == 41001) {
                    result.put("data", "查询接口受限，请联系管理员！");
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }*/

    static String md5(String str) {
        /**
         * Spring 中建议直接使用 DigestUtils.md5DigestAsHex() 计算md5值
         */
        try {
            byte[] secretBytes = null;
            secretBytes = MessageDigest.getInstance("md5").digest(str.getBytes());

            String md5code = new BigInteger(1, secretBytes).toString(16);
            for (int i = 0; i < 32 - md5code.length(); i++) {
                md5code = "0" + md5code;
            }
            return md5code;
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("没有这个md5算法！");
        }
    }


    static String sign(String code,String input) {
        return md5(code + input + APP_SECRET);
    }

    /**
     * 语言筛选器
     *
     * @param code
     * @param lang
     * @return
     */
    private String langConstructors(String code,String result, String lang) {
        String verif = "";
        switch (lang) {
            case "java":
                String resultStr = recursionUnusualJava(result);
                if (resultStr.contains("运行结束，执行耗时")){
                     verif = grammarVerificationForJave(code);
                    if (verif.equals("")) {
                        verif = "代码规范正常";
                    }
                    code.substring(0,code.indexOf("\n\n"));
                    code.substring(code.indexOf("执行耗时：")+5,code.length());
                }
                return resultStr + "\n" + verif;
            case "py":
                String resultPy = recursionUnusualPython(result);
                if (resultPy.contains("运行结束，执行耗时")){
                    verif = grammarVerificationForPython(code);
                    if (verif.equals("")) {
                        verif = "代码规范正常";
                    }
                }
                return resultPy + "\n" + verif;
            case "c":
                String resultCLang = recursionUnusualCLang(result);
                if (resultCLang.contains("运行结束，执行耗时")){
                    verif = grammarVerificationForCLang(code);
                    if (verif.equals("")) {
                        verif = "代码规范正常";
                    }
                }
                return resultCLang + "\n" + verif;
        }
        return null;
    }

    /**
     * 语言筛选器
     *
     * @param code
     * @param lang
     * @return
     */
    private Map langConstructorsToMap(String code, String result,String lang) {
        Map map = new HashMap();
        String verif = "";
        switch (lang) {
            case "java":
                String resultStr = recursionUnusualJava(result);
                if (resultStr.contains("运行结束，执行耗时")){
                    verif = grammarVerificationForJave(code);
                    if (verif.equals("")) {
                        verif = "代码规范正常";
                    }
                    map.put("verif",verif);
                    map.put("runResult",result.substring(0,result.indexOf("\n\n")));
                    map.put("violationNum",0);
                }else {
                    map.put("verif",resultStr);
                    map.put("runResult",result);
                    map.put("violationNum",1);
                }
                map.put("executeTime",result.substring(result.indexOf("执行耗时：")+5,result.length()));
                map.put("data",resultStr);
                return map;
            case "py":
                String resultPy = recursionUnusualPython(result);
                if (resultPy.contains("运行结束，执行耗时")){
                    verif = grammarVerificationForPython(code);
                    if (verif.equals("")) {
                        verif = "代码规范正常";
                    }
                    map.put("verif",verif);
                    map.put("runResult",result.substring(0,result.indexOf("\n\n")));
                    map.put("violationNum",0);
                }else {
                    map.put("verif",resultPy);
                    map.put("runResult",result);
                    map.put("violationNum",1);
                }
                map.put("executeTime",result.substring(result.indexOf("执行耗时：")+5,result.length()));
                map.put("data",resultPy);
                return map;
            case "c":
                String resultCLang = recursionUnusualCLang(result);
                if (resultCLang.contains("运行结束，执行耗时")){
                    verif = grammarVerificationForCLang(code);
                    if (verif.equals("")) {
                        verif = "代码规范正常";
                    }
                    map.put("verif",verif);
                    map.put("runResult",result.substring(0,result.indexOf("\n\n")));
                    map.put("violationNum",0);
                }else {
                    map.put("verif",resultCLang);
                    map.put("runResult",result);
                    map.put("violationNum",1);
                }
                map.put("executeTime",result.substring(result.indexOf("执行耗时：")+5,result.length()));
                map.put("data",resultCLang);
                return map;
        }
        return new HashMap();
    }

    /**
     * java
     * 当代码异常时，匹配异常进行友好提示
     *
     * @param code
     * @return
     */
    private String recursionUnusualJava(String code) {
        try {
            for (String key : UnusualCode.JAVA.keySet()) {
                if (code.contains(key)) {
                    String value = UnusualCode.JAVA.get(key);
                    if (code.contains("does not exist")) {
                        String errorSre = code.substring(code.indexOf("error"), code.indexOf("class"));
                        if (code.contains("Main.java:")) {
                            String errorRow = code.substring(code.indexOf("Main.java:"), code.indexOf(": error:") == -1 ? code.indexOf(")") : code.indexOf(": error:"));
                            return value + "\n错误大约在第：" + errorRow + "行";
                        }
                        return errorSre + "  " + value;
                    }
                    if (code.contains("Main.java:")) {
                        String errorRow = code.substring(code.indexOf("Main.java:"), code.indexOf(": error:") == -1 ? code.indexOf(")") : code.indexOf(": error:"));
                        return value + "\n错误大约在 第：" + errorRow + "行";
                    }
                    return value;
                }
            }
            return code;
        } catch (Exception e) {
            return "程序执行错误\n" + code;
        }
    }

    /**
     * Python
     * 当代码异常时，匹配异常进行友好提示
     *
     * @param code
     * @return
     */
    private String recursionUnusualPython(String code) {
        try {
            for (String key : UnusualCode.PYTHON.keySet()) {
                if (code.contains(key)) {
                    String value = UnusualCode.PYTHON.get(key);
                    if (code.contains("line")) {
                        String errorRow = code.substring(code.indexOf("line") + 5, code.indexOf(", in") == -1 ? code.indexOf("line") + 6 : code.indexOf(", in"));
                        if (code.contains("Non-ASCII character")) {
                            return value + "\n错误大约在第：" + errorRow + " 行";
                        }
                        String MistakesRow = code.substring(code.indexOf("in <module>") == -1 ? code.indexOf("line") + 6 : code.indexOf("in <module>") + 16, code.indexOf(key));
                        return value + "\n错误大约在第：" + errorRow + " 行 \n" + "错误内容为：" + MistakesRow;
                    }
                    return value;
                }
            }
            return code;
        } catch (Exception e) {
            return "程序执行错误\n" + code;
        }
    }

    /**
     * c语言
     * 当代码异常时，匹配异常进行友好提示
     *
     * @param code
     * @return
     */
    private String recursionUnusualCLang(String code) {
        try {
            for (String key : UnusualCode.CLang.keySet()) {
                if (code.contains(key)) {
                    String value = UnusualCode.CLang.get(key);
                    if (code.contains("error")) {
                        if (code.contains("missing terminating") || code.contains("expected declaration") || code.contains("expected identifier") || code.contains("expected '=', ',', ';', 'asm' or '__attribute__' before '{' token")) {
                            String codeArr[] = code.split("\n");
                            String errorRow = codeArr[0].substring(codeArr[0].indexOf("c:") + 2, codeArr[0].indexOf(": warning") - 2 == 1 ? codeArr[0].indexOf(": error:") : codeArr[0].indexOf(": error:") - 2);
                            Object row = errorRow.length() == 1 ? errorRow : errorRow.length() - 1;
                            return value + "\n错误大约在第：" + row + " 行 \n" + "错误内容为：" + codeArr[1];
                        }
                        if (code.contains("ld returned 1 exit status")) {
                            return value;
                        }
                        String codeArr[] = code.split("\n");
                        String errorRow = codeArr[1].substring(codeArr[1].indexOf("c:") + 2, codeArr[1].indexOf(": error:") - 2);
                        Object row = errorRow.length() == 1 ? errorRow : errorRow.length() - 1;
                        return value + "\n错误大约在第：" + row + " 行 \n" + "错误内容为：" + codeArr[2];
                    }
                    return value;
                }
            }
            return code;
        } catch (Exception e) {
            return "程序执行错误\n" + code;
        }
    }

    private String grammarVerificationForJave(String code) {
        String appraisals = "";
        for (String key : UnusualCode.JAVA_GRAMMAR_REGULARITY_CHECK.keySet()) {
            Pattern pattern = Pattern.compile(key);
            Matcher matcher = pattern.matcher(code);
            if (matcher.find()) {
                String value = UnusualCode.JAVA_GRAMMAR_REGULARITY_CHECK.get(key);
                appraisals += "\n" + value + "\n";
            }
        }
        return appraisals;
    }

    private String grammarVerificationForCLang(String code) {
        String appraisals = "";
        for (String key : UnusualCode.CLang_GRAMMAR_REGULARITY_CHECK.keySet()) {
            Pattern pattern = Pattern.compile(key);
            Matcher matcher = pattern.matcher(code);
            if (matcher.find()) {
                String value = UnusualCode.CLang_GRAMMAR_REGULARITY_CHECK.get(key);
                appraisals += "\n" + value + "\n";
            }
        }
        return appraisals;
    }

    private String grammarVerificationForPython(String code) {
        String appraisals = "";
        for (String key : UnusualCode.PYTHON_GRAMMAR_REGULARITY_CHECK.keySet()) {
            Pattern pattern = Pattern.compile(key);
            Matcher matcher = pattern.matcher(code);
            //当用于指示关键字参数或用于指示未注释的函数参数的默认值时，请勿在=号 周围使用空格
            if (code.contains("complex(")) {
                String regE = "\\s=\\s|\\s=\\w|\\w=\\s";
                Pattern patternC = Pattern.compile(regE);
                Matcher matcherC = patternC.matcher(code);
                if (matcherC.find()) {
                    appraisals += "当用于指示关键字参数或用于指示未注释的函数参数的默认值时，请勿在=号 周围使用空格\n";
                }
            }
            if (matcher.find()) {
                String value = UnusualCode.PYTHON_GRAMMAR_REGULARITY_CHECK.get(key);
                appraisals += "\n" + value + "\n";
            }
        }
        return appraisals;
    }


    static String sign(String code) {
        return Md5Util.md5(code + APP_SECRET);
    }
}
